Kotlin

Inline classes

Swift
                  inline class Password(val value: String)
                
                    inline class Password(let value: String)
                  
                  value class Password(private val s: String)
                
                    value class Password(private let s: String)
                  
                  // For JVM backends
@JvmInline
value class Password(private val s: String)
                
                    // For JVM backends
@JvmInline
value class Password(private let s: String)
                  
                  // No actual instantiation of class 'Password' happens
// At runtime 'securePassword' contains just 'String'
val securePassword = Password("Don't try this in production")
                
                    // No actual instantiation of class 'Password' happens
// At runtime 'securePassword' contains just 'String'
let securePassword = Password("Don't try this in production")
                  

Members

                  @JvmInline
value class Name(val s: String) {
    init {
        require(s.length > 0) { }
    }
​
    val length: Int
        get() = s.length
​
    fun greet() {
        println("Hello, $s")
    }
}    
​
fun main() {
    val name = Name("Kotlin")
    name.greet() // method `greet` is called as a static method
    println(name.length) // property getter is called as a static method
}
                
                    @JvmInline
value class Name(let s: String) {
    init {
        require(s.length > 0) { }
    }
​
    let length: Int
        get() = s.length
​
    func greet() {
        print("Hello, $s")
    }
}    
​
func main() {
    let name = Name("Kotlin")
    name.greet() // method `greet` is called as a static method
    print(name.length) // property getter is called as a static method
}
                  

Inheritance

                  interface Printable {
    fun prettyPrint(): String
}
​
@JvmInline
value class Name(val s: String) : Printable {
    override fun prettyPrint(): String = "Let's $s!"
}    
​
fun main() {
    val name = Name("Kotlin")
    println(name.prettyPrint()) // Still called as a static method
}
                
                    interface Printable {
    func prettyPrint() -> String
}
​
@JvmInline
value class Name(let s: String) : Printable {
    override func prettyPrint() -> String = "Let's $s!"
}    
​
func main() {
    let name = Name("Kotlin")
    print(name.prettyPrint()) // Still called as a static method
}
                  

Representation

                  interface I
​
@JvmInline
value class Foo(val i: Int) : I
​
fun asInline(f: Foo) {}
fun <T> asGeneric(x: T) {}
fun asInterface(i: I) {}
fun asNullable(i: Foo?) {}
​
fun <T> id(x: T): T = x
​
fun main() {
    val f = Foo(42) 
    
    asInline(f)    // unboxed: used as Foo itself
    asGeneric(f)   // boxed: used as generic type T
    asInterface(f) // boxed: used as type I
    asNullable(f)  // boxed: used as Foo?, which is different from Foo
    
    // below, 'f' first is boxed (while being passed to 'id') and then unboxed (when returned from 'id') 
    // In the end, 'c' contains unboxed representation (just '42'), as 'f' 
    val c = id(f)  
}
                
                    interface I
​
@JvmInline
value class Foo(let i: Int) : I
​
func asInline(f: Foo) {}
func <T> asGeneric(x: T) {}
func asInterface(i: I) {}
func asNullable(i: Foo?) {}
​
func <T> id(x: T): T = x
​
func main() {
    let f = Foo(42) 
    
    asInline(f)    // unboxed: used as Foo itself
    asGeneric(f)   // boxed: used as generic type T
    asInterface(f) // boxed: used as type I
    asNullable(f)  // boxed: used as Foo?, which is different from Foo
    
    // below, 'f' first is boxed (while being passed to 'id') and then unboxed (when returned from 'id') 
    // In the end, 'c' contains unboxed representation (just '42'), as 'f' 
    let c = id(f)  
}
                  

Mangling

                  @JvmInline
value class UInt(val x: Int)
​
// Represented as 'public final void compute(int x)' on the JVM
fun compute(x: Int) { }
​
// Also represented as 'public final void compute(int x)' on the JVM!
fun compute(x: UInt) { }
                
                    @JvmInline
value class UInt(let x: Int)
​
// Represented as 'public final void compute(int x)' on the JVM
func compute(x: Int) { }
​
// Also represented as 'public final void compute(int x)' on the JVM!
func compute(x: UInt) { }
                  

Calling from Java code

                  @JvmInline
value class UInt(val x: Int)
​
fun compute(x: Int) { }
​
@JvmName("computeUInt")
fun compute(x: UInt) { }
                
                    @JvmInline
value class UInt(let x: Int)
​
func compute(x: Int) { }
​
@JvmName("computeUInt")
func compute(x: UInt) { }
                  

Inline classes vs type aliases

                  typealias NameTypeAlias = String
​
@JvmInline
value class NameInlineClass(val s: String)
​
fun acceptString(s: String) {}
fun acceptNameTypeAlias(n: NameTypeAlias) {}
fun acceptNameInlineClass(p: NameInlineClass) {}
​
fun main() {
    val nameAlias: NameTypeAlias = ""
    val nameInlineClass: NameInlineClass = NameInlineClass("")
    val string: String = ""
​
    acceptString(nameAlias) // OK: pass alias instead of underlying type
    acceptString(nameInlineClass) // Not OK: can't pass inline class instead of underlying type
​
    // And vice versa:
    acceptNameTypeAlias(string) // OK: pass underlying type instead of alias
    acceptNameInlineClass(string) // Not OK: can't pass underlying type instead of inline class
}
                
                    typealias NameTypeAlias = String
​
@JvmInline
value class NameInlineClass(let s: String)
​
func acceptString(s: String) {}
func acceptNameTypeAlias(n: NameTypeAlias) {}
func acceptNameInlineClass(p: NameInlineClass) {}
​
func main() {
    let nameAlias: NameTypeAlias = ""
    let nameInlineClass: NameInlineClass = NameInlineClass("")
    let string: String = ""
​
    acceptString(nameAlias) // OK: pass alias instead of underlying type
    acceptString(nameInlineClass) // Not OK: can't pass inline class instead of underlying type
​
    // And vice versa:
    acceptNameTypeAlias(string) // OK: pass underlying type instead of alias
    acceptNameInlineClass(string) // Not OK: can't pass underlying type instead of inline class
}
                  

Gradle

                  kotlin {
    sourceSets.all {
        languageSettings.enableLanguageFeature('InlineClasses')
    }
}
                
                    kotlin {
    sourceSets.all {
        languageSettings.enableLanguageFeature('InlineClasses')
    }
}
                  
                  kotlin {
    sourceSets.all {
        languageSettings.enableLanguageFeature("InlineClasses")
    }
}
                
                    kotlin {
    sourceSets.all {
        languageSettings.enableLanguageFeature("InlineClasses")
    }
}
                  

Maven

                  <configuration>
    <args>
        <arg>-Xinline-classes</arg> 
    </args>
</configuration>
                
                    <configuration>
    <args>
        <arg>-Xinline-classes</arg> 
    </args>
</configuration>